package rz.dmc.effect;

import java.util.List;

import rz.dmc.util.RZPoint;
import rz.dmc.util.RZSegment;


import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

public class RZDrawingView extends View 
{
    private float currentStrokeWidth;

    private Paint paint = new Paint();
    private Path path = new Path();

    private float lastTouchX;
    private float lastTouchY;
    private final RectF dirtyRect = new RectF();

    private int currentColor;
    private int currentAlpha;
    private RZSegment currentSegment;
    private Path childPath = new Path();
    
    private Canvas buffer;
    private Bitmap bitmap;
    private Paint bitmapPaint;
    
    
    
    public RZDrawingView(Context context, AttributeSet attrs) 
    {
        super(context, attrs);

        currentColor = Color.BLUE;
        currentStrokeWidth = 5.0f;
        currentAlpha = 50; 
        
        paint.setAntiAlias(true);
        paint.setColor(currentColor);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeJoin(Paint.Join.ROUND);
        paint.setStrokeWidth(currentStrokeWidth);
        paint.setAlpha(currentAlpha);
        
        bitmapPaint = new Paint(Paint.DITHER_FLAG);
        bitmap = Bitmap.createBitmap(2000, 2000, Bitmap.Config.ARGB_8888);
        buffer = new Canvas(bitmap);
    }

    /**
     * Erases the signature.
     */
    public void clear() 
    {
    	buffer.drawColor(0, PorterDuff.Mode.CLEAR);
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) 
    {
    	canvas.drawBitmap(bitmap, 0, 0, bitmapPaint);
        canvas.drawPath(path, paint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) 
    {
        float eventX = event.getX();
        float eventY = event.getY();

        switch (event.getAction()) 
        {
            case MotionEvent.ACTION_DOWN:
            {
            	currentSegment = new RZSegment(currentColor, currentStrokeWidth); 
            	
                path.moveTo(eventX, eventY);
                lastTouchX = eventX;
                lastTouchY = eventY;
                currentSegment.addPoint(lastTouchX, lastTouchY);
            }
                return true;

            case MotionEvent.ACTION_MOVE:
                // Start tracking the dirty region.
                resetDirtyRect(eventX, eventY);

                // When the hardware tracks events faster than they are delivered, the
                // event will contain a history of those skipped points.
                int historySize = event.getHistorySize();
                for (int i = 0; i < historySize; i++) 
                {
                    float historicalX = event.getHistoricalX(i);
                    float historicalY = event.getHistoricalY(i);
                    expandDirtyRect(historicalX, historicalY);
                    path.lineTo(historicalX, historicalY);
                }

                // After replaying history, connect the line to the touch point.
                path.lineTo(eventX, eventY);
                currentSegment.addPoint(eventX, eventY);
                break;

            case MotionEvent.ACTION_UP:
            	path.lineTo(eventX, eventY);
            	buffer.drawPath(path, paint);
            	drawSegment(currentSegment, getPaintBySegmentInfo(currentSegment.getColor(), currentSegment.getStrokeWidth()));
            	path.reset();
            	break;
                
            default:
                Log.d("default", "Ignored touch event: " + event.toString());
                return false;
        }
        
        invalidate();

        lastTouchX = eventX;
        lastTouchY = eventY;

        return true;
    }

    /**
     * Called when replaying history to ensure the dirty region includes all
     * points.
     */
    private void expandDirtyRect(float historicalX, float historicalY) {
        if (historicalX < dirtyRect.left) {
            dirtyRect.left = historicalX;
        } else if (historicalX > dirtyRect.right) {
            dirtyRect.right = historicalX;
        }
        if (historicalY < dirtyRect.top) {
            dirtyRect.top = historicalY;
        } else if (historicalY > dirtyRect.bottom) {
            dirtyRect.bottom = historicalY;
        }
    }

    /**
     * Resets the dirty region when the motion event occurs.
     */
    private void resetDirtyRect(float eventX, float eventY) 
    {
        // The lastTouchX and lastTouchY were set when the ACTION_DOWN
        // motion event occurred.
        dirtyRect.left = Math.min(lastTouchX, eventX);
        dirtyRect.right = Math.max(lastTouchX, eventX);
        dirtyRect.top = Math.min(lastTouchY, eventY);
        dirtyRect.bottom = Math.max(lastTouchY, eventY);
    }

    
    private Paint getPaintBySegmentInfo(int color, float strokeWidth) 
    {
        Paint p = new Paint();
        p.setAntiAlias(true);
        p.setDither(true);
        p.setColor(color);
        p.setStyle(Paint.Style.STROKE);
        p.setStrokeWidth(strokeWidth);
        p.setStrokeJoin(Paint.Join.ROUND);
        
        return p;
    }
    
    private void drawSegment(RZSegment segment, Paint p) 
    {
        childPath.reset();
        List<RZPoint> points = segment.getPoints();
        RZPoint current = points.get(0);
        float width = segment.getStrokeWidth();
        
        childPath.moveTo(current.x * width, current.y * width);
        RZPoint next = null;
        
        for (int i = 1; i < points.size(); ++i) 
        {
            next = points.get(i);
            childPath.quadTo(current.x * width, current.y * width, ((next.x + current.x) * width) / 2, ((next.y + current.y) * width) / 2);
            current = next;
        }
        
        if (next != null) 
        {
            childPath.lineTo(next.x * width, next.y * width);
        }
        
        buffer.drawPath(childPath, p);
    }
    
    
	public void setColor(int newColor)
	{
		currentAlpha = 50;
		
		paint.setXfermode(null);
		paint.setAlpha(currentAlpha);
		
		currentColor = newColor;
		paint.setColor(currentColor);
	}

	public void setStroke(float strokeWidth)
	{
		currentStrokeWidth = strokeWidth;
		paint.setStrokeWidth(currentStrokeWidth);
	}
	
	public void setErase()
	{
		currentAlpha = 0;
		
		paint.setXfermode( new PorterDuffXfermode( PorterDuff.Mode.CLEAR ) );
		paint.setAlpha(currentAlpha);
	}
}
